// ------------------------------------------------------------------------------------
// REVISION LOG ENTRY
//
// File name  : tkit3.js
// Revision By: (C) Copyright 2014 Hiroyuki Tominaga, All Rights Reserved.
//
// Revised on : 2014-07-01 06:20:12
// Summary    : tkit-loader.js の実装により, tkit base script を書き換え.
//
// CDN        :
//    https://js-dragwindow.googlecode.com/svn/trunk/tkit3.js
//    https://raw.githubusercontent.com/motrohi/js-projects/master/js-tkit/tkit3.js
//
// Required   : jQuery, jsafx
//
// ------------------------------------------------------------------------------------

/**
 * class Tkit base.
 */
(function(name_space)
{
    var tkit, W; {
        W = (window || self);
        /* */
        tkit = W[name_space];
        (!tkit) && ( tkit = W[name_space] = {} );
    }

    // undefined is not a "undefined"
    // typeof is returns "string" value.
    // ----------------------------------- oop functions. --------------------------------
    /* need for oop extendOverride */
    var _EXTENDED_ = "_EXTENDED_";//$.generateId(6);

    function extendOverride(_this_clazz, _super) {
        var _this_prototype = _this_clazz.prototype;
        // overwrite _super argment
        _super = _super.prototype;

        for (var p in _super) {
            if(!_this_prototype[p]) // has not property?
                _this_prototype[p] = _super[p];
        }
        return _this_clazz;
    }
    //
    // verify Inheritance.
    //
    //
    function verifyInheritance(
        this_clazz, super_clazz
    ) {
        if (this_clazz === null)
            console.trace();
		// note: 2014-07-01 07:07:35 多重継承可能に
		// qname の定義は必須.
        var token = _EXTENDED_ + super_clazz.qname;
        if( !this_clazz[token] ) {
            extendOverride(
                this_clazz, super_clazz
            )[token] = true;
        }
    };

    /*
    [2010-11-25 11:17:32]
        継承するクラスは必ずこの関数を呼んで低レベルの初期化を行います。
        Initialize the class that inherits from the low level of this function must be called.
    */
    //
    // @param instance        :current instance of object. MUST this
    // @param this_clazz    :
    // @param super_clazz    :
    // @param args            :must be arguments object.
    //
    tkit.initAtom = function initAtom(
        instance,
        this_clazz, super_clazz,
        args
    ) {
        //acutual_ns.AfxObject.call(this, this_name);
        if(typeof(super_clazz) === "function")
            super_clazz.apply(instance, args);

        verifyInheritance(
            this_clazz, super_clazz
        );
    }
    // ----------------------------------- oop functions. --------------------------------
    /*
        void XMLHttpRequest.open(
            in AUTF8String method,
            in AUTF8String url,
            [optional] in boolean async,
            [optional] in AString user,
            [optional] in AString password
        );
    */
    tkit.loadAjax = function (src, callback, async/* optional */) {
        var _xhr = new XMLHttpRequest();
        _xhr.onreadystatechange = function() {
            if (this.readyState == XMLHttpRequest.DONE || this.status == 200) {
                if (typeof callback === "function") {
                    callback(this.responseText, this);
                }
            }
        };
        (async === undefined) && (async = false);
        _xhr.open('GET', src, async);
        _xhr.send();
    }

    // ----------------------------------- dynamic load. -----------------------------------
    // helper
    function _eval_script(resonseText, xhr) {
        W["eval"](resonseText);
    }
    /**
     * @param className the class name of create.
     * @param element   element id or element instance.
     */
	function createInstance(className) {
        // Object.create は匿名 class を作る.
        // よって, object instanceof Object しか成立しない.
        // しかし, constructor に作成元の function object を設定してやると
        // object instanceof ctor が成立するようになる.
        // この case で, Object.create を使うことの意義は,
        // function.apply を call することによる可変長 arguments の適用だろう.
        // new ctor とすると, arguments の展開が出来ない.
        var ctor = tkit.forName( Array.prototype.shift.call(arguments) );
        if (ctor === null) {
            return null;
        }
        var object = Object.create(ctor.prototype);
        ctor.apply(object, arguments);
        object.constructor = ctor
        return object;
    }

    /**
     * tkit2 までは class が存在しない場合 load schedule していたが,
     * tkit3 からは, 何もしなくなった.
     * この function が null を返した場合, Tkit.getLoader().schedule([className, element, config], [true]);
     * で load する使用に変更.
     * @param className the class name of create.
     */
    tkit.forName = function (className) {
    	var clazz = tkit[className];
        return (clazz !== undefined)? clazz: null;
    }
    tkit.exists = function (className) {
        return tkit.forName(className) !== null;
    }

    tkit.create = createInstance;
    // ----------------------------------- dynamic load class. --------------------------------

    // -------------------------------------------------------------------------------
    //
    //
    // -------------------------------------------------------------------------------
    /**
     * この function 完了後, setBindEventListener で set された listener は削除される.
     * @param cfg_id json を記述した element id.(innerText property を使用する
     *               または, data-tkit-classes に class name + ":" + config_id とすることで,
     *               個別に指定可能.( data-tkit-classes="DragWindow:dragwindow-cfg"
     *  json format:
     *  ------------------------------------------------------------
     *  {
     *      "<bind element id>": {
     *          <config for bind class>
     *      }
     *  }
     *  @param callback class が構築されるたびに instance が渡される.(必要ないときは function 以外の値を設定
     *                  function callback(tkit_class) {}
     *  @param var_name この値を指定すると, window[var_name] に load された class 参照が element id として bind される.
     *
     */
    tkit.bindClasses = function bindClasses(cfg_id, callback, var_name)
    {
        var tkit_elements = $("[data-tkit-class]");
        if (typeof callback !== "function")
            callback = $.noop;

        var json; { // cfg_id を指定しない場合, 空の json object となる.
            json = $dom(cfg_id);
            json = (json === null)? {}: JSON.parse(json.textContent);
        }

        // cache call back, for invoke later.
        var loader = tkit.getLoader();
        loader.addLoadListener(callback);
		// export to global var name.
		if (typeof var_name === "string")
			loader.exports( unHyphenate(var_name) );

        tkit_elements.each(
            function(i, e) {
                var fqdn = e.getAttribute("data-tkit-class").split(":");
                var config  = (fqdn.length === 2)? fqdn[1]: json;
                fqdn = fqdn[0];
                try {
                    var tkit_class = createInstance(fqdn, e); // class の load が必要な場合, 非同期で schedule される.
                    if (tkit_class) { // load されていない場合 null
                        tkit_class.setOptions(config);
                        loader.addInstance(tkit_class);
                    } else {
                        // schedule(array, in_develop, primary)
                        loader.schedule([fqdn, e, config], true);
                        tkit.DEBUG && console.log("class_queue=%o", loader.getQueue());
                    }
                } catch (e) {
                    console.log(e);
                }
            }
        );

		return loader.exports();
    };

    /**
     * base class である XWidget を直接継承する場合,
     * この shortcut function を利用可能.
     *
     * @param instance   MUST be "this".
     * @param this_clazz MUST be "this" constructor.
     * @param args       MUST be "arguments"
     *
     *    Tkit.extendBase(this, arguments);
     *
     * @see Tkit#initAtom(instance, this_clazz, super_clazz, arguments)
     */
    // note: 2014-06-25 15:56:49
    // 元は, extendBase(instance, args) だったが, instance.constructor を
    // this_clazz として使用するとうまくいかなかった...
    // note: 2014-07-01 07:02:46
    // class 定義ごとに, prototype に constuctor property を定義する方法もあるが,
    // 毎回, 定義しなくてはいけない...
    // しかし,
    tkit.extendBase = function extendBase(instance, this_clazz, args) {
        var super_clazz = tkit.XWidget;
        super_clazz.apply(instance, args);
        verifyInheritance( this_clazz, super_clazz );
    }

    /**
     * class Tkit.XWidget
     *
     * Tkit package base class.
     */
    tkit.buildClass(
		"XWidget", function (element) {
	        //this.attach(element); // これは error となった.
	        this._e = typeof element === "string"? $dom(element): element;
	    }, {
	        // to jQuery object.
	        $: function () { return $(this._e); },
	        attach: function (element) {
	            this._e = typeof element === "string"? $dom(element): element;
	            return this;
	        },
	        release: function () {
	            // If needed, override...
	            return this;
	        },
	        // 2014-07-01 03:27:59
	        id: function () {
	        	return this._e.id;
	        },
	        // 簡易 check function.
	        is: function(element) {
	            var e = this._e;
	            if (!e) return false;
	            return e.id === element || e === element;
	        },

	        install: $.noop,
	        updateOptions: $.noop,
	        /*
	        * html element に plain text として設定情報を記述する.
	        * 構文に関しては, class の実装ごとに異なる.
	        */
	        setOptions: function setOptions(config) {
	            if (typeof config === "string") { // means config element id
	                var e = $dom(config);
	                if (e !== null) {
	                    var src = e.getAttribute("data-src");
	                    if (src !== null && src.length > 0) {
	                        var _self = this;
	                        function load_done(text) {
	                            _self.updateOptions( JSON.parse(text) );
	                        };
	                        tkit.loadAjax(src, load_done);
	                    }
	                    else
	                        this.updateOptions( JSON.parse(e.innerText) );

	                    return;
	                } else { // json string?
	                    try {
	                        config = JSON.parse(config);
	                    } catch (e) {
	                        console.log(e);
	                    }
	                }
	            }
	             // json object.
	            if ($.isPlainObject(config))
	                this.updateOptions(config);
	        }
	    }
    );
    /*tkit.XWidget = function XWidget(element) {
        //this.attach(element); // これは error となった.
        this._e = typeof element === "string"? $dom(element): element;
    };
    tkit.XWidget.qname = name_space + ".XWidget";
    tkit.XWidget.prototype = {
        // to jQuery object.
        $: function () { return $(this._e); },
        attach: function (element) {
            this._e = typeof element === "string"? $dom(element): element;
            return this;
        },
        release: function () {
            // If needed, override...
            return this;
        },
        // 2014-07-01 03:27:59
        id: function () {
        	return this._e.id;
        },
        // 簡易 check function.
        is: function(element) {
            var e = this._e;
            if (!e) return false;
            return e.id === element || e === element;
        },

        install: $.noop,
        updateOptions: $.noop,

        setOptions: function setOptions(config) {
            if (typeof config === "string") { // means config element id
                var e = $dom(config);
                if (e !== null) {
                    var src = e.getAttribute("data-src");
                    if (src !== null && src.length > 0) {
                        var _self = this;
                        function load_done(text) {
                            _self.updateOptions( JSON.parse(text) );
                        };
                        tkit.loadAjax(src, load_done);
                    }
                    else
                        this.updateOptions( JSON.parse(e.innerText) );

                    return;
                } else { // json string?
                    try {
                        config = JSON.parse(config);
                    } catch (e) {
                        console.log(e);
                    }
                }
            }
             // json object.
            if ($.isPlainObject(config))
                this.updateOptions(config);
        }
    };*/


    // -------------------------------------------------------------------------------
    tkit.getRandomColor = function getRandomColor(rgb)
    {
        var c;
        if(rgb) {
            c = "rgb(" +
                Math.floor(Math.random() * 256) + ", " +
                Math.floor(Math.random() * 256) + ", " +
                Math.floor(Math.random() * 256) + ")";
        }
        else {
            c = "#";
            for(var i = 0; i < 3; i++) {
                var hex = Math.floor(Math.random() * 256).toString(16);
                c += hex.length === 2? hex: "0" + hex;
            }
        }
        return c;
    };

})("Tkit");    // (function()

